package avicit.bdp.dds.server.worker.task.ft;

import avicit.bdp.common.dto.StorageResourceConf;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.SpringApplicationContext;
import avicit.bdp.common.utils.uploads.FileAdapterUtils;
import avicit.bdp.dds.dispatch.enums.ExecutionStatus;
import avicit.bdp.dds.dispatch.task.AbstractParameters;
import avicit.bdp.dds.dispatch.task.ft.FTParameters;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.api.service.LoggerService;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.common.utils.OSUtils;
import avicit.bdp.dds.dao.entity.ProcessInstance;
import avicit.bdp.dds.dao.entity.TaskLog;
import avicit.bdp.dds.server.entity.TaskExecutionContext;
import avicit.bdp.dds.server.worker.task.AbstractTask;
import avicit.bdp.dds.server.worker.task.CommandExecuteResult;
import avicit.bdp.dds.server.worker.task.ShellCommandExecutor;
import avicit.bdp.dds.service.process.ProcessService;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 试飞数据处理调度节点
 * 针对命令行执行
 *
 * @author xugb
 * @date 2023/6/26 14:19
 */
public class FTTask extends AbstractTask {
    /**
     * taskExecutionContext
     */
    private TaskExecutionContext taskExecutionContext;

    private FTParameters ftParameters;

    private ProcessInstance instance;
    private ProcessService processService;
    private LoggerService loggerService;
    /**
     * shell command executor
     */
    private ShellCommandExecutor shellCommandExecutor;

    public FTTask(TaskExecutionContext taskExecutionContext, Logger logger) {
        super(taskExecutionContext, logger);
        this.taskExecutionContext = taskExecutionContext;
        this.processService = SpringApplicationContext.getBean(ProcessService.class);
        this.loggerService = SpringApplicationContext.getBean(LoggerService.class);
        this.shellCommandExecutor = new ShellCommandExecutor(this::logHandle,
                taskExecutionContext,
                logger);

    }

    @Override
    public void init() {
        logger.info("http task params {}", taskExecutionContext.getTaskParams());
        this.ftParameters = JSONObject.parseObject(taskExecutionContext.getTaskParams(), FTParameters.class);

        if (!ftParameters.checkParameters()) {
            throw new RuntimeException("ft task params is not valid");
        }

        instance = processService.findProcessInstanceById(taskExecutionContext.getProcessInstanceId());
    }

    @Override
    public void handle() throws Exception {
        String threadLoggerInfoName = String.format(Constants.TASK_LOG_INFO_FORMAT, taskExecutionContext.getTaskAppId());
        Thread.currentThread().setName(threadLoggerInfoName);
        try {
            handleLog("任务开始执行:\n");
            loggerService.sendWebsocketTaskStatus(instance.getProcessDefinitionId(), instance.getId(), ExecutionStatus.RUNNING_EXEUTION.getCode());
            CommandExecuteResult commandExecuteResult = shellCommandExecutor.run(buildCommand());

            if (commandExecuteResult.getExitStatusCode() == 0) {
                setExitStatusCode(Constants.EXIT_CODE_SUCCESS);
                //handleLog("任务运行成功");
                loggerService.sendWebsocketTaskStatus(instance.getProcessDefinitionId(), instance.getId(), ExecutionStatus.SUCCESS.getCode());
            } else {
                setExitStatusCode(Constants.EXIT_CODE_FAILURE);
                handleLog("任务运行失败");
                loggerService.sendWebsocketTaskStatus(instance.getProcessDefinitionId(), instance.getId(), ExecutionStatus.FAILURE.getCode());
            }

            setExitStatusCode(commandExecuteResult.getExitStatusCode());
            setAppIds(commandExecuteResult.getAppIds());
            setProcessId(commandExecuteResult.getProcessId());

        } catch (Exception e) {
            logger.error("FT task failure", e);
            //日志
            handleLog("任务运行失败:\n" + getExceptionInfo(e));
            instance.setState(ExecutionStatus.FAILURE.getCode());
            processService.updateProcessInstance(instance);
            setExitStatusCode(Constants.EXIT_CODE_FAILURE);
            loggerService.sendWebsocketTaskStatus(instance.getProcessDefinitionId(), instance.getId(), ExecutionStatus.FAILURE.getCode());
            throw e;
        }


    }

    /**
     * create command
     *
     * @return file name
     * @throws Exception exception
     */
    private String buildCommand() throws Exception {
        // generate scripts
        String fileName = String.format("%s/%s_node.%s",
                taskExecutionContext.getExecutePath(),
                taskExecutionContext.getTaskAppId(), OSUtils.isWindows() ? "bat" : "sh");

        Path path = new File(fileName).toPath();

        if (Files.exists(path)) {
            return fileName;
        }
        String json = ftParameters.getFtParams();
        Map<String, Object> ftMap = JSONUtils.toObjectMap(json);
        String cmd = ConfigUtils.getInstance().getString("sf.execFile", "sf_sdk_demo");
        String icdFilePath = MapUtils.getString(ftMap, "icdFilePath", "");
        String inputFilePath = MapUtils.getString(ftMap, "inputFilePath", "");
        String groupParams = MapUtils.getString(ftMap, "groupParams", "");
        String icdFileStartTime = MapUtils.getString(ftMap, "icdFileStartTime", "");
        String icdFileEndTime = MapUtils.getString(ftMap, "icdFileEndTime", "");
        String dbtable = MapUtils.getString(ftMap, "dbtable", "root.ln.test");
        dbtable = dbtable + "." + taskExecutionContext.getProcessInstanceId();
        String outputType = MapUtils.getString(ftMap, "outputType", "txt");
        String outputDir = MapUtils.getString(ftMap, "outputDir", "");
        String dylibDir = MapUtils.getString(ftMap, "dylibDir", "");
        String dylibName = MapUtils.getString(ftMap, "dylibName", "");
        String logDir = MapUtils.getString(ftMap, "logDir", "");

        Map<String, Object> configMap = new HashMap<>();
        //输入参数
        Map<String, Object> inputParamsMap = new HashMap<>();
        inputParamsMap.put("icdFilePath", icdFilePath);
        inputParamsMap.put("inputFilePath", inputFilePath);
        inputParamsMap.put("groupParams", groupParams);
        inputParamsMap.put("icdFileStartTime", icdFileStartTime);
        inputParamsMap.put("icdFileEndTime", icdFileEndTime);

        StorageResourceConf conf = FileAdapterUtils.getStorageResourceByProjectId(null);
        //oss对象存储
        inputParamsMap.put("inputType", "minio");
        inputParamsMap.put("endpoint", conf.getEndPoint());
        inputParamsMap.put("accessKey", conf.getAccessKey());
        inputParamsMap.put("secretKey", conf.getSecretKey());
        inputParamsMap.put("bucketName", conf.getStoragePath());
        //输出参数
        Map<String, Object> outputParamsMap = new HashMap<>();
        outputParamsMap.put("outputType", "iotdb");
        outputParamsMap.put("dbtable", dbtable);
        outputParamsMap.put("username", ConfigUtils.getInstance().getString("iotdb.username", "root"));
        outputParamsMap.put("password", ConfigUtils.getInstance().getString("iotdb.password", "root"));
        outputParamsMap.put("hosts", ConfigUtils.getInstance().getString("iotdb.hosts", "127.0.0.1:6667"));
        outputParamsMap.put("writeMaxSize", ConfigUtils.getInstance().getInteger("iotdb.writeMaxSize", 10));

        configMap.put("inputParams", inputParamsMap);
        configMap.put("outputParams", outputParamsMap);

        Set<PosixFilePermission> perms = PosixFilePermissions.fromString(Constants.RWXR_XR_X);
        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);

        String configFile = String.format("%s/%s_node.%s",
                taskExecutionContext.getExecutePath(),
                taskExecutionContext.getTaskAppId(), "json");
        Path configPath = new File(configFile).toPath();
        if (OSUtils.isWindows()) {
            Files.createFile(configPath);
        } else {
            Files.createFile(configPath, attr);
        }

        Files.write(configPath, JSONUtils.toJson(configMap).getBytes(), StandardOpenOption.APPEND);
        String script = cmd + " " + configFile;

        logger.info("task execute path : {}", taskExecutionContext.getExecutePath());
        logger.info("执行命令：" + script);

        if (OSUtils.isWindows()) {
            Files.createFile(path);
        } else {
            Files.createFile(path, attr);
        }

        Files.write(path, script.getBytes(), StandardOpenOption.APPEND);

        return fileName;
    }

    private void handleLog(String content) {
        TaskLog taskLog = new TaskLog();
        taskLog.setId(instance.getId());
        taskLog.setTaskName("系统");
        taskLog.setContents(content);
        loggerService.insertTaskLog(taskLog);
    }

    @Override
    public AbstractParameters getParameters() {
        return this.ftParameters;
    }

    public String getExceptionInfo(Exception ex) {
        StringBuilder messages = new StringBuilder();
        StackTraceElement[] message = ex.getStackTrace();
        for (StackTraceElement element : message) {
            messages.append("\t").append(element.toString()).append("\n");
        }
        return messages.toString();
    }
}
